package org.glut.wechat.chat.websocket;

import com.alibaba.fastjson2.JSON;
import jakarta.websocket.*;
import jakarta.websocket.server.HandshakeRequest;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.glut.wechat.chat.config.GetHttpSessionConfig;
import org.glut.wechat.chat.domain.Message;
import org.glut.wechat.chat.domain.User;
import org.glut.wechat.chat.service.IGroupService;
import org.glut.wechat.chat.service.IUserService;
import org.glut.wechat.chat.utils.JwtUtil;
import org.glut.wechat.chat.utils.MessageUtils;
import org.glut.wechat.chat.utils.SpringContextUtil;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.glut.wechat.chat.utils.RedisUtil;
/**
 * WebSocket聊天端点，处理用户间实时消息通信
 */
@ServerEndpoint(value = "/chat/{userId}", configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {

    // 存储在线用户的会话信息（用户ID -> 会话对象）
    private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();

    // 注入工具类和服务（通过Spring上下文获取）
    private static JwtUtil jwtUtil = SpringContextUtil.getBean(JwtUtil.class);
    private static IUserService userService = SpringContextUtil.getBean(IUserService.class);
    // 新增：注入Redis工具类
    private static RedisUtil redisUtil = SpringContextUtil.getBean(RedisUtil.class);

    // 新增：Redis存储聊天记录的键前缀
    private static final String CHAT_REDIS_KEY_PREFIX = "chat:history:";

    // 1. 新增群聊相关存储（在ChatEndpoint类顶部）
    private static final Map<String, Set<String>> groupMembers = new ConcurrentHashMap<>(); // 群ID→成员用户ID集合
    // 注入GroupService（复用SpringContextUtil）
    private static IGroupService groupService = SpringContextUtil.getBean(IGroupService.class);
    // 新增群聊Redis键前缀（与单聊区分，避免冲突）
    private static final String GROUP_CHAT_REDIS_KEY_PREFIX = "chat:group:history:";


    // 当前连接用户的ID和用户名
    private String currentUserId;
    private String currentUsername;

    /**
     * 处理WebSocket连接建立事件
     * 1. 从请求中获取身份令牌并验证
     * 2. 初始化当前用户信息并加入在线列表
     * 3. 广播在线用户列表更新
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config, @PathParam("userId") String pathUserId) {
        String token = null;

        // 获取握手请求信息（用于身份验证）
        HandshakeRequest handshakeRequest = (HandshakeRequest) config.getUserProperties().get(HandshakeRequest.class.getName());
        if (handshakeRequest == null) {
            System.err.println("握手请求信息为空，无法进行身份验证");
            closeSession(session, "握手请求信息为空");
            return;
        }

        // 从请求头获取Bearer令牌
        List<String> authHeaders = handshakeRequest.getHeaders().get("Authorization");
        if (authHeaders != null && !authHeaders.isEmpty()) {
            String authHeader = authHeaders.get(0);
            if (authHeader.startsWith("Bearer ")) {
                token = authHeader.substring(7).trim();
            }
        }

        // 解析并验证用户ID（优先从令牌获取，失败则尝试路径参数）
        Integer userId = parseUserId(token, pathUserId);
        if (userId == null) {
            closeSession(session, "用户身份验证失败");
            return;
        }

        // 验证用户存在性并初始化会话信息
        User user = userService.selectById(userId);
        if (user == null) {
            System.out.println("未查询到用户信息，用户ID：" + userId);
            closeSession(session, "用户不存在");
            return;
        }

        // 保存当前用户会话并广播在线状态更新
        this.currentUserId = userId.toString();
        this.currentUsername = user.getUsername();
        onlineUsers.put(currentUserId, session);
        broadcastAllUser(MessageUtils.getMessage(null, getFriends()));
    }

    /**
     * 解析用户ID（从令牌或路径参数）
     */
    // 后端ChatEndpoint类的parseUserId方法修改
    private Integer parseUserId(String token, String pathUserId) {
        // 1. 优先从Token解析userId（修复核心：用Token中的userId替代路径参数）
        if (token != null && jwtUtil.validateToken(token)) {
            try {
                return jwtUtil.getUserIdFromToken(token); // 从Token获取真实userId
            } catch (Exception e) {
                System.out.println("令牌解析失败：" + e.getMessage());
            }
        }
        // 2. Token解析失败时，才用路径参数（兼容旧逻辑）
        if (pathUserId != null && !pathUserId.isEmpty()) {
            try {
                return Integer.parseInt(pathUserId);
            } catch (NumberFormatException e) {
                System.out.println("路径参数userId格式错误：" + pathUserId);
            }
        }
        return null;
    }


    /**
     * 关闭会话并输出原因
     */
    private void closeSession(Session session, String reason) {
        try {
            session.close();
            System.out.println("连接已关闭：" + reason);
        } catch (IOException e) {
            System.err.println("关闭连接失败：" + e.getMessage());
        }
    }

    /**
     * 获取当前在线用户ID列表
     */
    public Set getFriends() {
        return onlineUsers.keySet();
    }

    /**
     * 广播消息给所有在线用户（异步发送避免状态冲突）
     */
    private void broadcastAllUser(String message) {
        for (Session session : onlineUsers.values()) {
            try {
                // 仅向打开状态的会话发送消息
                if (session.isOpen() && session.getAsyncRemote().getBatchingAllowed()) {
                    session.getAsyncRemote().sendText(message, result -> {
                        if (result.getException() != null) {
                            System.err.println("广播消息失败: " + result.getException().getMessage());
                        }
                    });
                }
            } catch (Exception e) {
                System.err.println("广播消息异常: " + e.getMessage());
            }
        }
    }

    /**
     * 处理WebSocket连接关闭事件
     * 1. 移除当前用户的在线状态
     * 2. 延迟广播在线用户列表更新（避免关闭过程中的状态冲突）
     */
    @OnClose
    public void onClose(Session session) {
        if (currentUserId != null) {
            onlineUsers.remove(currentUserId);
            System.out.println("用户下线：" + currentUserId);
            String message = MessageUtils.getMessage(null, getFriends());

            // 延迟广播，确保关闭操作完成
            new Thread(() -> {
                try {
                    Thread.sleep(100);
                    broadcastAllUser(message);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }).start();
        }
    }

    /**
     * 处理接收到的消息
     * 1. 解析消息并验证格式
     * 2. 向指定接收方发送消息（确保前端能识别的格式）
     */
    @OnMessage
    public void onMessage(String message) {
        try {
            Message msg = JSON.parseObject(message, Message.class);
            System.out.println("接收消息：groupId=" + msg.getGroupId() + ", 内容=" + msg.getMessage());

            if (msg == null) {
                System.out.println("消息解析失败");
                return;
            }

            String content = msg.getMessage();
            // 先校验消息内容是否为空（无论单聊/群聊都需要内容）
            if (content == null || content.trim().isEmpty()) {
                System.out.println("消息内容为空，忽略处理");
                return;
            }

            // -------------------------- 1. 优先处理群聊（无需toUserId） --------------------------
            String groupId = msg.getGroupId();
            if (groupId != null && !groupId.trim().isEmpty()) {
                groupId = groupId.trim();
                // 群聊只需校验群ID和内容（已通过内容校验）
                Set<String> members = groupService.getGroupMembers(groupId);
                if (members.isEmpty()) {
                    System.out.println("群不存在或无成员：" + groupId);
                    return;
                }
                // 构造群消息并转发（排除发送者自己）
                String sender = msg.getSenderName() != null ? msg.getSenderName() : currentUsername;
                String groupMsg = "{\"sender\":\"" + sender + "\",\"content\":\"" + content + "\",\"groupId\":\"" + groupId + "\",\"system\":false}";
                for (String memberId : members) {
                    if (memberId.equals(currentUserId)) continue; // 跳过自己
                    Session memberSession = onlineUsers.get(memberId);
                    if (memberSession != null && memberSession.isOpen()) {
                        memberSession.getBasicRemote().sendText(groupMsg);
                    }
                }
                // 存储群聊记录
                saveGroupMessageToRedis(groupId, currentUserId, sender, content);
                return; // 群聊处理完毕，直接返回
            }

            // -------------------------- 2. 再处理单聊（需要toUserId） --------------------------
            String toUserId = msg.getToName();
            if (toUserId == null || toUserId.trim().isEmpty()) {
                System.out.println("单聊消息缺少接收方ID，忽略处理");
                return;
            }
            toUserId = toUserId.trim();
            // 发送单聊消息
            Session toSession = onlineUsers.get(toUserId);
            if (toSession != null && toSession.isOpen()) {
                String responseMsg = "{\"sender\":\"" + currentUsername + "\",\"content\":\"" + content + "\",\"system\":false}";
                toSession.getBasicRemote().sendText(responseMsg);
            }
            // 存储单聊记录
            saveMessageToRedis(currentUserId, currentUsername, toUserId, content);

        } catch (Exception e) {
            System.out.println("消息处理失败：" + e.getMessage());
            e.printStackTrace();
        }
    }


    /**
     * 新增：将聊天消息保存到Redis
     */
    private void saveMessageToRedis(String senderId, String senderName, String receiverId, String content) {
        try {
            // 生成Redis key（使用小ID:大ID的形式，确保A-B和B-A的聊天记录在同一个key下）
            int sender = Integer.parseInt(senderId);
            int receiver = Integer.parseInt(receiverId);
            String smallId = sender < receiver ? senderId : receiverId;
            String bigId = sender > receiver ? senderId : receiverId;
            String redisKey = CHAT_REDIS_KEY_PREFIX + smallId + ":" + bigId;

            // 构造消息对象
            Map<String, Object> chatMsg = new HashMap<>();
            chatMsg.put("senderId", senderId);
            chatMsg.put("senderName", senderName);
            chatMsg.put("receiverId", receiverId);
            chatMsg.put("content", content);
            chatMsg.put("time", System.currentTimeMillis()); // 消息发送时间戳

            // 存储到Redis，设置7天过期时间
            redisUtil.lRightPush(redisKey, JSON.toJSONString(chatMsg), 7 * 24 * 3600);
        } catch (Exception e) {
            System.err.println("保存消息到Redis失败：" + e.getMessage());
            e.printStackTrace();
        }
    }
    /**
     * 处理WebSocket错误事件
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        System.err.println("WebSocket错误：" + throwable.getMessage());
        throwable.printStackTrace();
    }


    // 3. 新增：群聊消息存储到Redis的方法
    private void saveGroupMessageToRedis(String groupId, String senderId, String senderName, String content) {
        try {
            // 群聊Redis键：前缀+群ID（无需区分成员，一个群一个key）
            String redisKey = GROUP_CHAT_REDIS_KEY_PREFIX + groupId;
            // 构造群聊消息结构（比单聊多groupId）
            Map<String, Object> chatMsg = new HashMap<>();
            chatMsg.put("senderId", senderId);
            chatMsg.put("senderName", senderName);
            chatMsg.put("groupId", groupId);
            chatMsg.put("content", content);
            chatMsg.put("time", System.currentTimeMillis());

            // 存储+7天过期（复用RedisUtil）
            redisUtil.lRightPush(redisKey, JSON.toJSONString(chatMsg), 7 * 24 * 3600);
        } catch (Exception e) {
            System.err.println("保存群聊消息到Redis失败：" + e.getMessage());
        }
    }
}
